// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package edwards25519

import "sync"

// basepointTable是一组32个affineLookupTables，其中表i是从256i*basepoint生成的
// 。它是第一次使用时预先计算的。
func basepointTable() *[32]affineLookupTable {
	basepointTablePrecomp.initOnce.Do(func() {
		p := NewGeneratorPoint()
		for i := 0; i < 32; i++ {
			basepointTablePrecomp.table[i].FromP3(p)
			for j := 0; j < 8; j++ {
				p.Add(p, p)
			}
		}
	})
	return &basepointTablePrecomp.table
}

var basepointTablePrecomp struct {
	table    [32]affineLookupTable
	initOnce sync.Once
}

// ScalarBaseMult集合v=x*B，其中B是标准生成器，
// 返回v.
// 
// 标量乘法在恒定时间内完成。
func (v *Point) ScalarBaseMult(x *Scalar) *Point {
	basepointTable := basepointTable()

	// 写x=sum（x_i*16^i）so x*B=sum（B*x_i*16^i）
	// 如Ed25519论文中所述x_62*16^62*B 
	// /+x_1*16^1*B+x_3*16^3*B+…+x_63*16^63*B 
	// x*B=x_0*16^0*B+x_2*16^2*B+…+x_62*16^62*B 
	// /+16*（x_1*16^0*B+x_3*16^2*B+…+x_63*16^62*B）
	// 
	// 我们对每个i使用一个查找表来获得x_i*16^（2*i）*B 
	// 并进行四次乘法16。
	digits := x.signedRadix16()

	multiple := &affineCached{}
	tmp1 := &projP1xP1{}
	tmp2 := &projP2{}

	// 首先累加奇数分量
	v.Set(NewIdentityPoint())
	for i := 1; i < 64; i += 2 {
		basepointTable[i/2].SelectInto(multiple, digits[i])
		tmp1.AddAffine(v, multiple)
		v.fromP1xP1(tmp1)
	}

	// 乘以16 
	tmp2.FromP3(v)       // tmp2=v在P2坐标
	tmp1.Double(tmp2)    // tmp1=2*v在P1xP1坐标
	tmp2.FromP1xP1(tmp1) // tmp2=2*v在P2坐标
	tmp1.Double(tmp2)    // tmp1=4*v在P1xP1坐标
	tmp2.FromP1xP1(tmp1) // tmp2=4*v在P2坐标
	tmp1.Double(tmp2)    // tmp1=16*v在P1xP1坐标中
	v.fromP1xP1(tmp1)    // 现在v=16*（奇数分量）

	// 累加偶数分量
	for i := 0; i < 64; i += 2 {
		basepointTable[i/2].SelectInto(multiple, digits[i])
		tmp1.AddAffine(v, multiple)
		v.fromP1xP1(tmp1)
	}

	return v
}

// ScalarMult集合v=x*q，并返回v。
// 
// 标量乘法在恒定时间内完成。
func (v *Point) ScalarMult(x *Scalar, q *Point) *Point {
	checkInitialized(q)

	var table projLookupTable
	table.FromP3(q)

	// Write x=sum（x_i*16^i）
	// so x*Q=sum（Q*x_i*16^i）
	// /=Q*x_0+16*（Q*x_1+16*（…+Q*x_63）
	// <------由内而外计算----
	// 
	// 我们使用查找表获得x_i*Q值
	// 并进行四次加倍以计算16*Q 
	digits := x.signedRadix16()

	// 展开第一次循环迭代以保存计算16*identity 
	multiple := &projCached{}
	tmp1 := &projP1xP1{}
	tmp2 := &projP2{}
	table.SelectInto(multiple, digits[63])

	v.Set(NewIdentityPoint())
	tmp1.Add(v, multiple) // tmp1=x_63*Q在P1xP1坐标中
	for i := 62; i >= 0; i-- {
		tmp2.FromP1xP1(tmp1) // tmp2=（prev）在P2坐标中在P1xP1坐标系中tmp1=2*（prev）在P1xP1坐标系中tmp2=2*（prev）在P2坐标系中tmp2=2*（prev）在P1xP1坐标系中tmp1=4*（prev）在P1xP1坐标系中tmp1=113*（prev）在P2坐标系中tmp2=4*（prev）在P1xP1坐标系中tmp1=8*（prev）在P1xP1坐标系中tmp1=115*（prev）在P2坐标系中tmp2=8*（prev）在P2坐标系中
		table.SelectInto(multiple, digits[i])
		tmp1.Add(v, multiple) // tmp1=x_i*Q+16*（上一个）在P1xP1坐标系
	}
	v.fromP1xP1(tmp1)
	return v
}

// basepointNafTable是该基点的nafLookupTable8。
// 它在第一次使用时是预先计算的。
func basepointNafTable() *nafLookupTable8 {
	basepointNafTablePrecomp.initOnce.Do(func() {
		basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint())
	})
	return &basepointNafTablePrecomp.table
}

var basepointNafTablePrecomp struct {
	table    nafLookupTable8
	initOnce sync.Once
}

// VarTimeDoubleScalarBaseMult设置v=a*a+b*b，其中b是规范的
// 生成器，返回v.
// 
// 执行时间取决于输入。
func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point {
	checkInitialized(A)

	// 与单变量基方法类似，我们计算
	// 位，并将其用于查找表。但是，因为我们可以进行可变时间运算，所以我们不需要固定时间查找或固定时间数字计算。
	// 
	// 所以我们使用一个宽度为w的非相邻形式，而不是
	// 基数16。这就像一个二进制表示法（每个二进制位置有一个数字
	// 但是我们允许数字在
	// 大小上增长到2^{w-1}，这样非零数字就尽可能地稀疏。直观地说，这会将标量的
	// “质量”压缩到稀疏系数上（意味着
	// 更少的加法）。

	basepointNafTable := basepointNafTable()
	var aTable nafLookupTable5
	aTable.FromP3(A)
	// 因为基点是固定的，我们可以使用更宽的NAF 
	// 对应一个更大的表。
	aNaf := a.nonAdjacentForm(5)
	bNaf := b.nonAdjacentForm(8)

	// 找到第一个非零系数。
	i := 255
	for j := i; j >= 0; j-- {
		if aNaf[j] != 0 || bNaf[j] != 0 {
			break
		}
	}

	multA := &projCached{}
	multB := &affineCached{}
	tmp1 := &projP1xP1{}
	tmp2 := &projP2{}
	tmp2.Zero()

	// 从高位移到低位，在每次迭代时将累加器
	// 加倍，并检查是否存在非零
	// 系数以查找其倍数。
	for ; i >= 0; i-- {
		tmp1.Double(tmp2)

		// 只有在有非零系数的情况下才更新v。
		if aNaf[i] > 0 {
			v.fromP1xP1(tmp1)
			aTable.SelectInto(multA, aNaf[i])
			tmp1.Add(v, multA)
		} else if aNaf[i] < 0 {
			v.fromP1xP1(tmp1)
			aTable.SelectInto(multA, -aNaf[i])
			tmp1.Sub(v, multA)
		}

		if bNaf[i] > 0 {
			v.fromP1xP1(tmp1)
			basepointNafTable.SelectInto(multB, bNaf[i])
			tmp1.AddAffine(v, multB)
		} else if bNaf[i] < 0 {
			v.fromP1xP1(tmp1)
			basepointNafTable.SelectInto(multB, -bNaf[i])
			tmp1.SubAffine(v, multB)
		}

		tmp2.FromP1xP1(tmp1)
	}

	v.fromP2(tmp2)
	return v
}
